using System;
using System.Collections.Generic;
using MSOutlook = Microsoft.Office.Interop.Outlook;
using System.Reflection;

namespace Microsoft.Sample.Outlook.Utility
{
	/// <summary>
	/// Class to cache the reflection data of a specific type for purposes of sync with Outlook
	/// </summary>
	internal sealed class SyncInfo
	{
		private MSOutlook.OlItemType _itemType;
		Dictionary<PropertyInfo, OutlookFieldSyncAttribute> _syncProps = new Dictionary<PropertyInfo, OutlookFieldSyncAttribute>();

		public MSOutlook.OlItemType ItemType
		{
			get { return _itemType; }
		}

		public IDictionary<PropertyInfo, OutlookFieldSyncAttribute> SyncProperties
		{
			get { return _syncProps; }
		}

		private SyncInfo(MSOutlook.OlItemType itemType)
		{
			_itemType = itemType;
		}

		private static Dictionary<Type, SyncInfo> _dictTypeSyncInfo = new Dictionary<Type, SyncInfo>();

		public static SyncInfo GetSyncInfo(Type itemType)
		{
			if (_dictTypeSyncInfo.ContainsKey(itemType))
			{
				return _dictTypeSyncInfo[itemType];
			}

			OutlookItemSyncAttribute[] arItemSync =
				(OutlookItemSyncAttribute[])itemType.GetCustomAttributes(
					typeof(OutlookItemSyncAttribute), false);

			if (arItemSync.Length != 1)
				throw new ApplicationException("Invalid Type used for sync info");

			SyncInfo sync = new SyncInfo(arItemSync[0].ItemType);

			OutlookCustomSyncInfoAttribute[] arCustomSyncInfo =
				(OutlookCustomSyncInfoAttribute[])itemType.GetCustomAttributes(
					typeof(OutlookCustomSyncInfoAttribute), false);

			if (arCustomSyncInfo.Length == 0)
				PopulateSyncProperties(itemType, sync);
			else
			{
				MethodInfo mi = itemType.GetMethod(
					arCustomSyncInfo[0].MethodName,
					BindingFlags.Static | BindingFlags.Public);

				IDictionary<string, OutlookFieldSyncAttribute> mapProps = 
					(IDictionary<string, OutlookFieldSyncAttribute>)mi.Invoke(null, null);

				PopulateSyncProperties(itemType, sync, mapProps);
			}

			return sync;
		}

		private static void PopulateSyncProperties(Type itemType, SyncInfo sync, IDictionary<string, OutlookFieldSyncAttribute> mapProps)
		{
			foreach (string propName in mapProps.Keys)
			{
				PropertyInfo propInfo = itemType.GetProperty(propName, BindingFlags.Public | BindingFlags.Instance);
				sync.SyncProperties.Add(propInfo, mapProps[propName]);
			}
		}

		private static void PopulateSyncProperties(Type itemType, SyncInfo sync)
		{
			PropertyInfo[] arPropInfo = itemType.GetProperties();
			foreach (PropertyInfo propInfo in arPropInfo)
			{
				OutlookFieldSyncAttribute[] arFieldSync =
					(OutlookFieldSyncAttribute[])propInfo.GetCustomAttributes(
						typeof(OutlookFieldSyncAttribute),
						false);

				if (arFieldSync.Length == 1)
					sync.SyncProperties.Add(propInfo, arFieldSync[0]);
			}

			_dictTypeSyncInfo.Add(itemType, sync);
		}
	}


}